package controle;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Date;
import java.util.Random;
import java.util.Stack;
import modelo.Ninis;

/**
 *
 * @author root
 */
public class Ninef {

    private RandomAccessFile arquivo;
    private ControleSuperBloco superbloco;
    private Bitmap bmNinis;
    private Bitmap bmDados;
    private Stack<Pasta> diretorio;
    private String caminho;
    private byte[] clear;

    /**
     * @throws FileNotFoundException Não foi encontrado nenhum pendrive
     */
    public Ninef() throws FileNotFoundException, IOException {

        caminho = new String();
        this.arquivo = new RandomAccessFile(new File("/dev/sdb1"), "rw");
        superbloco = new ControleSuperBloco(arquivo);
        bmNinis = new Bitmap(arquivo, superbloco.entradasNinis, superbloco.bytesPorBloco);
        bmDados = new Bitmap(arquivo, superbloco.blocosparaUso, superbloco.deslocamentoBitmap);
        diretorio = new Stack<Pasta>();

        try {
            ControleNinis ninis = new ControleNinis(arquivo, superbloco.deslocamentoNinis);
            Pasta barra = new Pasta(arquivo, (superbloco.deslocamentoAreaDados + (ninis.posicaoBlocos[0] * superbloco.bytesPorBloco)));
            diretorio.push(barra);
        } catch ( Exception ex) {
            if ( ex instanceof NegativeArraySizeException ) {
                System.out.println("Como o software não reconheceu o sistema de arquivo, uma formatação foi necessária");
                this.formata(1024);
            }
        }
    }

    /**
     * @throws IOException 
     */
    public void formata(int bytesPorBloco) throws IOException {

        ControleNinis ninis;

        superbloco.atualizar(bytesPorBloco);

        clear = new byte[superbloco.bytesPorBloco];
        for (int i = 0; i < clear.length; i++) {
            clear[i] = 0;
        }

        arquivo.seek(superbloco.bytesPorBloco);
        for (int i = 0; i < superbloco.blocosReservados; i++) {
            arquivo.write(clear);
        }

        bmNinis = new Bitmap(arquivo, superbloco.entradasNinis, superbloco.bytesPorBloco);
        bmDados = new Bitmap(arquivo, superbloco.blocosparaUso, superbloco.deslocamentoBitmap);

        // IGNORAR O PRIMEIRO BIT 
        bmDados.primeiroDisponivel();
        //bmNinis.primeiroDisponivel();

        ninis = new ControleNinis(arquivo, (superbloco.deslocamentoNinis + (bmNinis.primeiroDisponivel() << 6)));
        ninis.tamanho = superbloco.bytesPorBloco;
        ninis.dataCriacao = (new Date()).getTime();
        ninis.dataModificacao = ninis.dataCriacao;
        ninis.extencao = "pas".getBytes();
        ninis.posicaoBlocos[0] = bmDados.primeiroDisponivel();
        ninis.atualizar();

        arquivo.seek((superbloco.deslocamentoAreaDados + (ninis.posicaoBlocos[0] * superbloco.bytesPorBloco)));
        arquivo.write(clear);

        Pasta barra = new Pasta(arquivo, (superbloco.deslocamentoAreaDados + (ninis.posicaoBlocos[0] * superbloco.bytesPorBloco)));
        diretorio.clear();
        diretorio.push(barra);
    }

    public void novaPasta(String nomePasta) throws IOException {

        int deslocamento = bmNinis.primeiroDisponivel();

        ControleNinis ninis = new ControleNinis(arquivo, (superbloco.deslocamentoNinis + (deslocamento << 6)));
        ninis.tamanho = superbloco.bytesPorBloco;
        ninis.dataCriacao = (new Date()).getTime();
        ninis.dataModificacao = ninis.dataCriacao;
        ninis.extencao = "pas".getBytes();
        ninis.posicaoBlocos[0] = bmDados.primeiroDisponivel();
        ninis.atualizar();

        diretorio.peek().inserir(nomePasta, deslocamento, true);
        arquivo.seek((superbloco.deslocamentoAreaDados + (ninis.posicaoBlocos[0] * superbloco.bytesPorBloco)));
        arquivo.write(clear);
    }

    public void abrirPasta(String nomePasta) throws IOException, IllegalAccessException {
        if (nomePasta.equals("..")) {
            diretorio.pop();
            caminho = caminho.substring(0, caminho.lastIndexOf("/"));
            caminho = caminho.substring(0, caminho.lastIndexOf("/") + 1);
            return;
        }

        int deslocamento = diretorio.peek().getNinisOf(nomePasta);
        if (deslocamento == -1)
            throw new IndexOutOfBoundsException();
        ControleNinis ninis = new ControleNinis(arquivo, superbloco.deslocamentoNinis + (deslocamento << 5));
        
        if (  ! diretorio.peek().isPasta(nomePasta) ) throw new IllegalAccessException();
        
        Pasta a = new Pasta(arquivo, (superbloco.deslocamentoAreaDados + (ninis.posicaoBlocos[0] * superbloco.bytesPorBloco)));
        diretorio.push(a);
        caminho += nomePasta + "/";
    }
    
    public void copiardoNinef(String arquivoOrigem, String caminhoDestino) throws FileNotFoundException, IOException, IllegalAccessException {
        
        int idNinis = diretorio.peek().getNinisOf(arquivoOrigem);
        if ( diretorio.peek().isPasta(arquivoOrigem) ) throw new IllegalAccessException(); 
        if ( idNinis == -1 ) throw new FileNotFoundException();
        
        File destino = new File (caminhoDestino + '/' + arquivoOrigem);
        
        if ( !destino.createNewFile() ) throw new StringIndexOutOfBoundsException();
        
        RandomAccessFile file = new RandomAccessFile (destino, "rw");
        
        ControleNinis inf = new ControleNinis(arquivo, superbloco.deslocamentoNinis + ( idNinis << 6 ));
        
        float a;
        int counter = 0;
        int nNinis = 0;
        
        a = inf.tamanho / (float)superbloco.bytesPorBloco;
        
        int nBlocos = ( a % 1 == 0 ) ? (int)a : (int)(a + 1) ;
        int[] posicaoBlocos = new int[nBlocos];
        byte[] bloco = new byte[superbloco.bytesPorBloco];
        
        a = (float) nBlocos / 8;
        
        ControleNinis[] ninis = new ControleNinis[( a % 1 == 0 ) ? (int)a : (int)(a + 1)];
        ninis[0] = inf;
        
        for ( int i = 1; i < ninis.length; i ++ )
            ninis[i] = new ControleNinis(arquivo, superbloco.deslocamentoNinis + ( ninis[i-1].posicaoBlocos[8] << 6 ));
        
        for ( int i = 0; i < nBlocos; i ++ )
            posicaoBlocos[i] = ninis[ i >> 3 ].posicaoBlocos[ i % 8 ];
        
        for ( int i = 0; i < nBlocos; i ++ ) {
            arquivo.seek((superbloco.deslocamentoAreaDados + (posicaoBlocos[i] * superbloco.bytesPorBloco)));
            arquivo.read(bloco);
            file.write(bloco);
        }
    }

    public void copiarparaNinef(String caminhoArquivo) throws FileNotFoundException, IOException {
        File file = new File (caminhoArquivo);
        RandomAccessFile arquivoOrigem = new RandomAccessFile( file, "r");

        float a;
        int counter = 0;
        int nNinis = 0;
        
        a = arquivoOrigem.length() / (float)superbloco.bytesPorBloco;
        
        int nBlocos = ( a % 1 == 0 ) ? (int)a : (int)(a + 1) ;
        int[] posicaoBlocos = new int[nBlocos];
        byte[] bloco = new byte[superbloco.bytesPorBloco];
        
        a = (float) nBlocos / 8;
        
        ControleNinis[] ninis = new ControleNinis[( a % 1 == 0 ) ? (int)a : (int)(a + 1)];
        
        for (int i = 0; i < nBlocos; i++) {
            posicaoBlocos[i] = bmDados.primeiroDisponivel();
            if ( nBlocos == i + 1  ) {
                arquivo.seek((superbloco.deslocamentoAreaDados + (posicaoBlocos[i] * superbloco.bytesPorBloco)));
                arquivo.write(clear);
            }
            arquivoOrigem.read(bloco);
            arquivo.seek((superbloco.deslocamentoAreaDados + (posicaoBlocos[i] * superbloco.bytesPorBloco)));
            arquivo.write(bloco);
        }

        for (int i = 0; ( i < ninis.length ) && ( counter < posicaoBlocos.length ); i ++ ) {
            nNinis = bmNinis.primeiroDisponivel();
            ninis[i] = new ControleNinis(arquivo, superbloco.deslocamentoNinis + (nNinis << 6));
            for (int j = 0; ( j < 8 ) && ( counter < posicaoBlocos.length ) ; j++ ) {
                ninis[i].posicaoBlocos[j] = posicaoBlocos[counter];
                counter++;
            }
            if ( i > 0 )
                ninis[ i - 1 ].posicaoBlocos[8] = nNinis;
            else {
                ninis[i].tamanho = file.length();
                ninis[i].dataCriacao = file.lastModified();
                ninis[i].dataModificacao = file.lastModified();
                ninis[i].extencao = "xxx".getBytes();
                diretorio.peek().inserir(caminhoArquivo.substring(caminhoArquivo.lastIndexOf("/") + 1, caminhoArquivo.length()), nNinis, false);
            }
        }
        
        for (int i = 0; i < ninis.length ; i ++ ) ninis[i].atualizar();
    }

    public ControleSuperBloco getSuperBloco() {
        return superbloco;
    }

    public String getBloco(int posicao) throws IOException {
        Bitmap a = new Bitmap(arquivo, superbloco.bytesPorBloco << 3, (superbloco.deslocamentoAreaDados + (superbloco.bytesPorBloco * posicao)));
        return a.toString();
    }

    public String getDiretorio() {
        return caminho;
    }

    public String getBitmapNinis() {
        return bmNinis.toString();
    }

    public String getBitmapDados() {
        return bmDados.toString();
    }

    public String listar() {
        return diretorio.peek().listar();
    }

    public String getNinis(int index) throws IOException {
        String r = new String();
        Ninis a;

        a = new ControleNinis(arquivo, superbloco.deslocamentoNinis + (index << 6));
        r += "[" + index + "][" + a.tamanho + "][" + new String(a.extencao) + "][" + (new Date(a.dataCriacao)).toLocaleString() + "][";
        for (int j = 0; j < 9; j++) {
            r += a.posicaoBlocos[j] + " ";
        }
        r += "]";

        return r;
    }
}
